package ru.spb.osll.myfit.server.handler;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

import ru.spb.osll.myfit.common.Content;
import ru.spb.osll.myfit.common.Include;
import ru.spb.osll.myfit.common.IncludeError;
import ru.spb.osll.myfit.common.Page;
import ru.spb.osll.myfit.common.PageElement;
import ru.spb.osll.myfit.common.PageError;
import ru.spb.osll.myfit.common.Table;
import ru.spb.osll.myfit.common.TableStyle;
import ru.spb.osll.myfit.common.TextParsingTools;
import ru.spb.osll.myfit.server.Converter;
import ru.spb.osll.myfit.server.parser.PageParser;
import ru.spb.osll.myfit.server.parser.PageUtils;
import ru.spb.osll.myfit.server.parser.RecursiveIncludeException;
import ru.spb.osll.myfit.server.parser.TableUtils;
import ru.spb.osll.myfit.util.IOUtils;

public class PageLoader {
  private static Logger LOG = Logger.getLogger(PageLoader.class);
  private TableUtils m_tableUtils;
  public PageLoader() {
  }
  
  public PageLoader(TableUtils tableUtils){
    m_tableUtils = tableUtils;
  }

  public Page loadPage(String root, String fileName, String pagePath) throws FileNotFoundException{
    Page page = new Page();
    try {
      page = createPage(root, pagePath, new LinkedList<String>(), false);
    }catch (RecursiveIncludeException e) {
    }
    return page;
  }

  public Page loadIncludePage(String root, String parentPagePath,
                                     String includePagePath) throws FileNotFoundException, RecursiveIncludeException {
    ArrayList<String> allPath = new ArrayList<String>();
    allPath.add(parentPagePath);
    return  createPage(root, includePagePath, allPath, true);
  }

  private Page createPage(String root, String pagePath, List<String> allPath, 
                          boolean isIncludePage)
      throws RecursiveIncludeException, FileNotFoundException {
    Page page = new Page();
    PageParser parser = new PageParser(new Converter());

    if (isRecursiveInclude(pagePath, allPath)) {
      allPath.add(pagePath);
      throw new RecursiveIncludeException(getRecursivePath(allPath));
    } else {

      try {
        List<String> lines = readPageFile(root, pagePath);
        List<PageElement> pageElements = parser
            .parsePage(new ArrayList<String>(lines));
        for (PageElement element : pageElements) {

          if (element instanceof Include) {
            ArrayList<String> forNextInclude = new ArrayList<String>(allPath);
            forNextInclude.add(pagePath);
            try {
              Page inludePage = createPage(root, ((Include) element).getPath(),
                  forNextInclude, true);
              ((Include) element).setPage(inludePage);
              page.insert(element);
            } catch (RecursiveIncludeException exeption) {
              if (isIncludePage) {
                throw exeption;
              } else {
                String message = TextParsingTools.RECURSIVE_CALL + pagePath +
                    " from the page " + exeption.getMessage();
                page.insert(new IncludeError(((Include) element).getPath(),
                    createPageWithError(message)));

              }
            } catch (FileNotFoundException e) {
              page.insert(new IncludeError(((Include) element).getPath(),
                  createPageWithError(e.getMessage())));
            }

          } else if (element instanceof Content) {
            Content content = createContent(root, pagePath,
                ((Content) element).getLevel());
            page.insert(content);
          } else if(element instanceof Table){
            setTableStyle((Table)element);
            page.insert(element);
          } else{
            page.insert(element);
          }
        }

      } catch (FileNotFoundException e) {
        throw e;
      } catch (IOException e) {
        LOG.error("Can't read page ", e);
      }
    }
    return page;
  }

 
  public Page createPageWithError(String errorMessage) {
    Page result = new Page();
    result.insert(createPageError(errorMessage));

    return result;
  }

  private PageError createPageError(String msg) {
    PageError pageError = new PageError();
    pageError.setMessage(msg);
    return pageError;
  }

  private String getRecursivePath(List<String> allPath) {
    String result = new String();
    int size = allPath.size();
    for (int i = 1; i < size; ++i) {
      result += allPath.get(i);
      result += (i != size - 1) ? " ⇾ " : " ";
    }
    return result;
  }

  private boolean isRecursiveInclude(String includePath, List<String> allPaths) {
    if (allPaths.contains(includePath)) {
      return true;
    }

    if (includePath.startsWith(".")) {
      String pathWithoutPoint = includePath.substring(1);
      if (allPaths.contains(pathWithoutPoint)) {
        return true;
      }
    } else {
      String pathWithPoint = "." + includePath;
      if (allPaths.contains(pathWithPoint)) {
        return true;
      }
    }
    return false;
  }
  
  private void setTableStyle(Table table){
      if(m_tableUtils != null){
        TableStyle style = m_tableUtils.getTableStyle(table);
        table.setStyle(style);
      }
  }

  public List<String> readPageFile(String root, String pagePath) throws IOException,
      FileNotFoundException {
    String fileName = PageUtils.getFullPath(root, pagePath);
    LOG.info("Loading page, path: " + fileName);
    return IOUtils.readFile(fileName);

  }

  public Content createContent(String root, String pagePath, Integer level) {
    ContentCreator creator = new ContentCreator(root);
    return creator.createContent(pagePath, level);
  }
}
